package com.xx.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xx.common.constant.Constants;
import com.xx.common.exception.ServiceException;
import com.xx.utils.base.MPageUtils;
import com.xx.utils.giu.GraphUtils;
import com.xx.utils.redis.RedisUtil;
import com.xx.web.domain.dto.ShipTrackDto;
import com.xx.web.domain.entity.ShipTrack;
import com.xx.web.mapper.ShipTrackMapper;
import com.xx.web.service.IShipTrackService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xx
 * @since 2023-03-17
 */
@Service
@RequiredArgsConstructor
public class ShipTrackServiceImpl extends ServiceImpl<ShipTrackMapper, ShipTrack> implements IShipTrackService {

    private final ShipTrackMapper mapper;

    /**
     * 分页列表
     *
     * @param shipTrack 根据需要进行传值
     * @return
     */
    @Override
    public IPage<ShipTrack> page(ShipTrack shipTrack) {

        QueryWrapper<ShipTrack> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                //
                .eq(shipTrack.getTrackId() != null, ShipTrack::getTrackId, shipTrack.getTrackId())
                // 唯一标识
                .eq(!StringUtils.isEmpty(shipTrack.getId()), ShipTrack::getId, shipTrack.getId())
                // 船名
                .eq(!StringUtils.isEmpty(shipTrack.getName()), ShipTrack::getName, shipTrack.getName())
                // 更新时间
                .eq(shipTrack.getTime() != null, ShipTrack::getTime, shipTrack.getTime())
                // 数据版本
                .eq(shipTrack.getVersion() != null, ShipTrack::getVersion, shipTrack.getVersion())
                // 经度
                .eq(shipTrack.getLon() != null, ShipTrack::getLon, shipTrack.getLon())
                // 纬度
                .eq(shipTrack.getLat() != null, ShipTrack::getLat, shipTrack.getLat())
                // 航速
                .eq(shipTrack.getSog() != null, ShipTrack::getSog, shipTrack.getSog())
                // 航向
                .eq(shipTrack.getCog() != null, ShipTrack::getCog, shipTrack.getCog())
                // 艏向
                .eq(shipTrack.getHeading() != null, ShipTrack::getHeading, shipTrack.getHeading())
                // 英文船名
                .eq(!StringUtils.isEmpty(shipTrack.getEnglishName()), ShipTrack::getEnglishName,
                        shipTrack.getEnglishName())
                // timeout
                .eq(shipTrack.getTimeout() != null, ShipTrack::getTimeout, shipTrack.getTimeout())
                // alarms
                .eq(shipTrack.getAlarms() != null, ShipTrack::getAlarms, shipTrack.getAlarms())
                // region
                .eq(!StringUtils.isEmpty(shipTrack.getRegion()), ShipTrack::getRegion, shipTrack.getRegion())
                // fileId
                .eq(!StringUtils.isEmpty(shipTrack.getFileId()), ShipTrack::getFileId, shipTrack.getFileId())
                // shipNo
                .eq(!StringUtils.isEmpty(shipTrack.getShipNo()), ShipTrack::getShipNo, shipTrack.getShipNo())
                // tags
                .eq(shipTrack.getTags() != null, ShipTrack::getTags, shipTrack.getTags())
                // track
                .eq(shipTrack.getTrack() != null, ShipTrack::getTrack, shipTrack.getTrack());

        IPage<ShipTrack> page = page(MPageUtils.mpPage(), queryWrapper);
        return page;
    }


    @Override
    public List<ShipTrack> trackList(ShipTrackDto shipTrack) {
        //计算经纬度
        GraphUtils.calcLongitudeAndLatitude(shipTrack, shipTrack.getPointList());
        return mapper.trackList(shipTrack);
    }

    /**
     * 详情
     *
     * @param id
     * @return
     */
    @Override
    public ShipTrack info(Long id) {
        return getById(id);
    }

    /**
     * 新增
     *
     * @param shipTrack 根据需要进行传值
     * @return
     */
    @Override
    public boolean add(ShipTrack shipTrack) {
        return save(shipTrack);
    }

    /**
     * 修改
     *
     * @param shipTrack 根据需要进行传值
     * @return
     */
    @Override
    public boolean modify(ShipTrack shipTrack) {
        return updateById(shipTrack);
    }

    /**
     * 删除(单个条目)
     *
     * @param id
     * @return
     */
    @Override
    public boolean remove(Long id) {
        return removeById(id);
    }

    /**
     * 删除(多个条目)
     *
     * @param ids
     * @return
     */
    @Override
    public boolean removes(List<Long> ids) {
        return removeByIds(ids);
    }

    private final RedisUtil redisUtil;

    @Override
    public List<List<Object>> history(String shipId, Double hours) {
        if (!StringUtils.hasText(shipId) || hours <= 0 || hours > 48) {
            throw new ServiceException("参数错误!");
        }
        List<List<Object>> list = new ArrayList<>();
        //当前时间
        LocalDateTime now = LocalDateTime.now();
        //今日零点时间
        LocalDateTime zero = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
        Duration between = Duration.between(zero, now);
        //现在距离今天零点时间 毫秒
        long todayZeroMill = between.toMillis();
        //查询间隔时间 毫秒
        int allMill = (int) (hours * 3600 * 1000);
        //相距的小时差
        long h = between.toHours();

        if (48 >= hours && hours > (24 + h)) {
            long yesterdayMill = allMill - todayZeroMill;
            long beforeZeroMill = (yesterdayMill - 24 * 3600 * 1000);
            beforeYesterday(beforeZeroMill, zero, shipId, list);
            yesterday(yesterdayMill, zero, shipId, list);
            today(allMill, now, shipId, list);
        } else if ((24 + h) >= hours && hours > h) {
            long yesterdayMill = allMill - todayZeroMill;
            yesterday(yesterdayMill, zero, shipId, list);
            today(todayZeroMill, now, shipId, list);
        } else if (hours <= h) { //true 表示查询在今天
            today(allMill, now, shipId, list);
        }
        return list;
    }

    private void beforeYesterday(long beforeZeroMill, LocalDateTime zero, String shipId, List<List<Object>> list) {
        //表示大前天的时间
        long yesterdayZeroMill = zero.minusDays(1).toInstant(ZoneOffset.ofHours(8)).toEpochMilli() - 1000;
        //计算距昨天零点之前多少毫秒
        long start = yesterdayZeroMill - beforeZeroMill;
        String key = Constants.SHIP_TRACK_KEY + LocalDate.now().minusDays(2) + ":" + shipId;
        //List<String> objects = redisUtil.rangeByScore1_4(key, start/1000, yesterdayZeroMill/1000);
        List<Object> objects = getTrackList(start, yesterdayZeroMill, key);
        list.add(objects);
    }


    private void yesterday(long yesterdayMill, LocalDateTime zero, String shipId, List<List<Object>> list) {
        //表示昨天的时间
        //计算距今天零点之前多少毫秒
        long zeroMill = zero.toInstant(ZoneOffset.ofHours(8)).toEpochMilli() - 1000;
        long start = zeroMill - yesterdayMill;
        String key = Constants.SHIP_TRACK_KEY + LocalDate.now().minusDays(1) + ":" + shipId;
        //List<Object> objects = redisUtil.rangeByScore1_8(key, start / 1000, zeroMill / 1000);
        List<Object> objects = getTrackList(start, zeroMill, key);
        list.add(objects);
    }

    private void today(long mill, LocalDateTime now, String shipId, List<List<Object>> list) {
        //获取当前时间的毫秒数
        long end = now.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
        long start = end - mill;
        String key = Constants.SHIP_TRACK_KEY + LocalDate.now() + ":" + shipId;
        //List<Object> objects = redisUtil.rangeByScore1_8(key, start / 1000, end / 1000);
        List<Object> objects = getTrackList(start, end, key);
        list.add(objects);
    }

    private List<Object> getTrackList(long score, long score1, String key) {
        long l = redisUtil.zZCard(key);
        List<Object> objects = new ArrayList<>();
        if (l == 0) {
            return objects;
        } else if (l < 100) {
            objects = redisUtil.rangeByScoreN(key, score / 1000, score1 / 1000, 2);
        } else if (l < 500) {
            objects = redisUtil.rangeByScoreN(key, score / 1000, score1 / 1000, 8);
        } else if (l < 2000) {
            objects = redisUtil.rangeByScoreN(key, score / 1000, score1 / 1000, 16);
        } else {
            objects = redisUtil.rangeByScoreN(key, score / 1000, score1 / 1000, 24);
        }
        return objects;
    }

    @Override
    public Map<String, Integer> historyStatistics(Integer days) {
        if (days < 1 || days > 30) {
            throw new RuntimeException("时间超出范围");
        }
        LocalDate now = LocalDate.now();
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < days; i++) {
            String key = Constants.SHIP_TRACK_KEY + now.minusDays(i) + ":MMSI:*";
            Set<String> set = redisUtil.keys(key);
            map.put(key, set.size());
        }
        return map;
    }
}
